﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;

namespace конфигуратор
{
    public class Поле : INotifyPropertyChanged
    {
        public  ДинамикОбъект ДинОбъект;
        public FieldInfo F { get; set;}
        public int НомерНаследника { get; set; }
        public string ИмяТипаОпределившегоПоле { get { return F.DeclaringType.Name; } }
        public string ИмяПоля { get { return F.Name; } }
        public object Значение
        {
            get
            {
                try
                {
                    return F.GetValue( ДинОбъект.Объект );
                }
                catch ( Exception )
                {
                    return null;
                }
            }
            set
            {
                try
                {
                    object значение;
                    if ( value is string )
                    {
                        var тип = ДинОбъект.Объект == null ? F.FieldType : F.GetValue( ДинОбъект.Объект );

                        значение = Парсер.Преобразовать((string) value, тип.GetType());
                    }
                    else  
                    {
                        значение = value;
                    }
                    
                    F.SetValue( ДинОбъект.Объект, значение );

                    ДинОбъект.ВызватьМетод( "OnPropertyChanged",  ""  );
                }
                catch
                {
                }
                   ДинОбъект.Обновить();
            }
        }
        public override string ToString ()
        {
            return F.Name + " : " + Значение;
        }        
        public string[]  СписокПеречислителя
        {
            get
            {
                if (!F.FieldType.IsEnum) return null;
                return Enum.GetNames( F.FieldType);
            }
        }
        public static List<Поле> ДатьСписокПолей ( ДинамикОбъект динобъект )
        {
            var списокПолей = new List<Поле>();
            if ( динобъект == null || динобъект.Тип ==null ) return null;

            var тип = динобъект.Тип;
            if(! тип.IsArray)
            ПолучитьНаследованныеПоля( тип, списокПолей, динобъект );
           

            return списокПолей;
        }
    
         static int ПолучитьНаследованныеПоля ( Type тип, List<Поле> списокПолей, 
             ДинамикОбъект владелец )
         {
             int номер=0;
             if( тип==typeof( object ))
             {
                 номер = 0;
             }
             else
             {
                   var t2 = тип.BaseType;
                   if ( t2 != null ) 
                 номер = ПолучитьНаследованныеПоля( t2, списокПолей, владелец )+1;
             }
           
              FieldInfo[] массив = тип.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly );
                foreach ( var fieldInfo in массив )
                {
                    списокПолей.Add( new Поле() { ДинОбъект = владелец, F = fieldInfo , НомерНаследника = номер} );
                }
                FieldInfo[] статПоля = тип.GetFields( BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly );
                foreach ( var fieldInfo in статПоля )
                {
                    списокПолей.Add( new Поле() { ДинОбъект = владелец, F = fieldInfo, НомерНаследника = номер} );
                }
                 return номер;

        }


        public string АтрибутыДоступа
        {
            get
            {  
                if ( F.IsPrivate ) return "private";      
                if ( F.IsPublic ) return "public";
                if ( F.IsFamilyOrAssembly ) return "protected internal";
                if ( F.IsAssembly ) return "internal";
                if ( F.IsFamily) return "protected";
                return "";
              }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void Обновить()
        {
            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs( "" ) );
        }
    }





    public class Свойство : INotifyPropertyChanged
    {
        public ДинамикОбъект ДинОбъект;
        public PropertyInfo СвойствоИнфо { get; set; }
        public int НомерНаследника { get; set; }
        public object Значение
        {
            get {
                try
                {
                    return СвойствоИнфо.GetValue(ДинОбъект.Объект, null);
                }
                catch (Exception)
                {
                    return null;
                }
            }
            set                                                                                    
            {
                try
                {
                    object значение;
                    if ( value is string )
                    {
                        var тип = ДинОбъект.Объект == null ? СвойствоИнфо.PropertyType : СвойствоИнфо.GetValue( ДинОбъект.Объект,null );
                        значение = Парсер.Преобразовать( ( string ) value, тип.GetType() );
                    }
                    else  
                    {
                        значение = value;
                    }
                    СвойствоИнфо.SetValue(ДинОбъект.Объект, значение,null);
                    ДинОбъект.ВызватьМетод( "OnPropertyChanged", "" );
                }
                catch
                {
                   
                }
                 ДинОбъект.Обновить();
            }
        }
        public string[] СписокПеречислителя
        {
            get
            {
                if ( !СвойстваМетода.ReturnType.IsEnum ) return null;
                return Enum.GetNames( СвойстваМетода.ReturnType );
            }
        }
        public static  List<Свойство> ДатьСписокСвойств ( ДинамикОбъект динОбъект)
        {
            var списокСвойств = new List<Свойство>();
            if ( динОбъект == null || динОбъект.Тип == null ) return списокСвойств;

            var тип = динОбъект.Тип;
           
            ПолучитьСвойства(тип, списокСвойств,динОбъект);
            return списокСвойств;
        }
         static int ПолучитьСвойства ( Type тип, List<Свойство> списокСвойств, ДинамикОбъект владелец )
        {
            int номер=0;
            if ( тип == typeof( object ) ) номер = 0;
            else
            {
                  var t2 = тип.BaseType;
                if (t2!=null)
                  номер = ПолучитьСвойства( t2, списокСвойств, владелец )+1;  
            }

            var массив = тип.GetProperties( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly );
            foreach ( var свойство in массив )
            {
                списокСвойств.Add( new Свойство() { ДинОбъект = владелец, СвойствоИнфо = свойство, НомерНаследника = номер } );
            }
            var статСвойства = тип.GetProperties( BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly );
            foreach ( var fieldInfo in статСвойства )
            {
                списокСвойств.Add( new Свойство() { ДинОбъект = владелец, СвойствоИнфо = fieldInfo, НомерНаследника = номер } );
            }
            return номер;
        }
         
        public  MethodInfo СвойстваМетода { get { return СвойствоИнфо.GetAccessors(true)[0]; } }
        public bool ЯвляетсяИндексатором()
        {
            var ff = СвойствоИнфо.GetIndexParameters();
            return ff.Length != 0;
        }
        public string ПараметрыИндексатора { get
        {
            var ff = СвойствоИнфо.GetIndexParameters();
            if ( ff.Length == 0 ) return "";
            string параметры = "";

            foreach ( ParameterInfo параметр in ff )
            {

                параметры +=  параметр.ParameterType.Name +  " " + параметр.Name +  ",";
            }
            if ( параметры.Length != 0 ) параметры = параметры.Remove( параметры.Length - 1 );

            параметры = "[ " + параметры + " ]";
            return параметры;
        } }
        public string АтрибутыДоступа
         {
             get
             {
                 string строкаАтрибутов = "";
                 var атрибутыМетода = СвойствоИнфо.GetAccessors(true)[0].Attributes;
                 if ( атрибутыМетода.HasFlag( MethodAttributes.FamORAssem ) ) строкаАтрибутов= "protected internal";
                 else
                 {
                     if ( атрибутыМетода.HasFlag( MethodAttributes.Assembly ) ) строкаАтрибутов= "internal";
                     else if ( атрибутыМетода.HasFlag( MethodAttributes.Private ) )
                         строкаАтрибутов= "private";
                     else if ( атрибутыМетода.HasFlag( MethodAttributes.Public ) )
                     {
                         строкаАтрибутов= "public";
                     }
                     else if ( атрибутыМетода.HasFlag( MethodAttributes.Family ) )
                     {
                         строкаАтрибутов= "protected";
                     }

                 }
                 //строкаАтрибутов += СвойствоИнфо.GetAccessors(true)[0].IsStatic ? " static" : "";
                 return строкаАтрибутов;
             }
         }
        public string АтрибутыЧтенияЗаписи
        {
            get { return "{" + (СвойствоИнфо.CanRead ? "get;" : "") + (СвойствоИнфо.CanWrite ? "set;" : "") + "}"; }
        }
        public  string АтрибутПерезаписи {get
        {
            if ( СвойстваМетода.IsAbstract ) return "abstract";
            if ( СвойстваМетода.IsVirtual & !СвойстваМетода.IsFinal )
            {
                var ti = СвойстваМетода.GetBaseDefinition();

                if ( ti != СвойстваМетода )
                    return "override";

                return "virtual";
            }
            if ( СвойстваМетода.IsVirtual & СвойстваМетода.IsFinal )
            {
                var ti = СвойстваМетода.GetBaseDefinition();

                if ( ti != СвойстваМетода )
                    return "override sealed";

                      return "virtual final";
            }

            if ( СвойстваМетода.IsHideBySig )
            {
                if ( СвойстваМетода.Attributes.HasFlag(MethodAttributes.NewSlot) )
               return "new";
            }
            return "";
        }}

        public event PropertyChangedEventHandler PropertyChanged;
        public void Обновить ()
        {
            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs( "" ) );
        }
    }
    




    public class Метод
    {
        public ДинамикОбъект ДинОбъект;
        public MethodInfo МетодИнфо { get; set; }
          public string АтрибутыДоступа {
            get
            {
                var атрибутыМетода = МетодИнфо.Attributes;
                if ( атрибутыМетода.HasFlag( MethodAttributes.FamORAssem ) ) return "protected internal";
                else
                {
                    if ( атрибутыМетода.HasFlag( MethodAttributes.Assembly ) ) return "internal";
                    else if ( атрибутыМетода.HasFlag( MethodAttributes.Private ) )
                        return "private";
                    else if ( атрибутыМетода.HasFlag( MethodAttributes.Public ) )
                    {
                        return "public";
                    }
                    else if ( атрибутыМетода.HasFlag( MethodAttributes.Family ) )
                    {
                        return "protected";
                    }

                }
                return "";
            } 
        }
        public static List<Метод> ДатьСписокМетодов(   ДинамикОбъект динОбъект)
        {
            var результирующийСписок = new List<Метод>();
            if ( динОбъект==null|| динОбъект.Тип == null ) return null;
            var СписокМетодов = динОбъект.Тип.GetMethods( BindingFlags.Public | 
                BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance );
            foreach (var methodInfo in СписокМетодов)
            {
                //if ( !(methodInfo.Name.Contains( "get_" ) || methodInfo.Name.Contains( "set_" ) || methodInfo.Name.Contains( "add_" ) || methodInfo.Name.Contains( "remove_" )) )
                результирующийСписок.Add(new Метод() {ДинОбъект = динОбъект, МетодИнфо = methodInfo});
            }
            return результирующийСписок;
              
        }
      
        public override string ToString ()
        {
            
            string сигнатура="";

            string строкаАтрибутов = "";
            строкаАтрибутов += АтрибутыДоступа + " " + (МетодИнфо.IsStatic ? "static " : "");

            сигнатура += строкаАтрибутов + " " + МетодИнфо.ReturnType.Name + " " + МетодИнфо.DeclaringType.Name+"."+
                               МетодИнфо.Name;
            
            string параметры = "";

            foreach ( ParameterInfo параметр in МетодИнфо.GetParameters() )
            {
                var парам = new Параметр() {ПараметрИнфо = параметр};
                параметры += парам.АтрибутыПараметра+" "+ параметр.ParameterType.Name +  
                   
                    " " + параметр.Name +(параметр.IsOptional? "="+параметр.DefaultValue.ToString():"") + ",";
            }
            if ( параметры.Length != 0 ) параметры = параметры.Remove( параметры.Length - 1 );

            параметры = "( "+параметры+ " )";
            сигнатура += параметры;
            return сигнатура;
        }
    }

    public class Конструктор
    {
        public ДинамикОбъект ДинОбъект;
        public ConstructorInfo КонструкторИнфо { get; set; }
        public List<Параметр> Параметры { get; set; }
        private Конструктор( ДинамикОбъект дин, ConstructorInfo кон)
        {
            ДинОбъект = дин;
            КонструкторИнфо = кон;

            Параметры = КонструкторИнфо.GetParameters().Select( 
                p => new Параметр { ПараметрИнфо = p } ).ToList();
        }
        public object СоздатьОбъект( object[] параметры)
        {
            return КонструкторИнфо.Invoke(параметры);
        }

        public static List<Конструктор> ДатьСписокКонструкторов( ДинамикОбъект динОбъект)
        {
            if (динОбъект.Тип == null ) return null;
            var списокКонструкторв = динОбъект.Тип.GetConstructors(
                BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
            return списокКонструкторв.Select( 
                constructorInfo => 
                    new Конструктор(динОбъект, constructorInfo)
                ).ToList();
        }
        
        
        public string АтрибутыДоступа
        {
            get
            {
                if ( КонструкторИнфо.IsPrivate ) return "private";
                if ( КонструкторИнфо.IsPublic ) return "public";
                if ( КонструкторИнфо.IsFamilyOrAssembly ) return "protected internal";
                if ( КонструкторИнфо.IsAssembly ) return "internal";
                if ( КонструкторИнфо.IsFamily ) return "protected";
                return "";
              
            }
        }
        public string СинтаксисПараметров
        {
            get
            {
                string параметры = "";

                foreach ( var параметр in Параметры )
                {
                    параметры += параметр + ",";
                }
                if ( параметры.Length != 0 ) параметры = параметры.Remove( параметры.Length - 1 );
                return параметры;
            }
        }
        public override string ToString ()
        {

            string сигнатура = "";

            string строкаАтрибутов = "";
            var атрибутыМетода = КонструкторИнфо.Attributes;
            строкаАтрибутов += АтрибутыДоступа + " " + (КонструкторИнфо.IsStatic ? "static " : "");

            сигнатура += строкаАтрибутов + " "  + КонструкторИнфо.DeclaringType.Name ;

            сигнатура += "( " + СинтаксисПараметров + " )";
            return сигнатура;
        }
    }

    public class Параметр
    {
         //public ДинамикОбъект ДинОбъект;
         public ParameterInfo ПараметрИнфо { get; set; }

         public string АтрибутыПараметра 
         {
             get
             {
                string s = "";
             if ( ПараметрИнфо.IsOut ) s += "out";
             //if (параметрИнфо.IsOptional)
             if ( ПараметрИнфо.IsRetval ) s += "ref";
             if ( ПараметрИнфо.IsIn ) s += "in";

             return s; 
             }
         }

         public override string ToString ()
         {
             return АтрибутыПараметра + " " + ПараметрИнфо.ParameterType.Name +

                    " " + ПараметрИнфо.Name + (ПараметрИнфо.IsOptional ? "=" + ПараметрИнфо.DefaultValue : "");
         }

    }

    public class  Событие
    {
        public ДинамикОбъект ДинОбъект;
        public EventInfo СобытиеИнфо;

        public static List<Событие> ДатьСписокСобытий( ДинамикОбъект динОбъект)
        {
            var рез = new List<Событие>();
            if ( динОбъект==null|| динОбъект.Тип == null ) return null;

            var тип = динОбъект.Тип;
            if ( !тип.IsArray )
                ПолучитьНаследованныеСобытия( тип, рез, динОбъект );
            
            return рез;
        }

        static void ПолучитьНаследованныеСобытия ( Type тип, List<Событие> списокПолей, ДинамикОбъект владелец )
        {

            if ( тип == typeof( object ) ) return;
            var t2 = тип.BaseType;
            if ( t2 != null ) ПолучитьНаследованныеСобытия( t2, списокПолей, владелец );

            var массив = тип.GetEvents( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly );
            foreach ( var fieldInfo in массив )
            {
                списокПолей.Add( new Событие() { ДинОбъект = владелец, СобытиеИнфо = fieldInfo } );
            }
            var статПоля = тип.GetEvents( BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly );
            foreach ( var fieldInfo in статПоля )
            {
                списокПолей.Add( new Событие() { ДинОбъект = владелец, СобытиеИнфо = fieldInfo } );
            }


        }


        public string АтрибутыДоступа
        {
            get
            {

                var атрибуты = СобытиеИнфо.GetRemoveMethod(true).Attributes;
                if ( атрибуты.HasFlag( MethodAttributes.FamORAssem ) ) return "protected internal";
                else
                {
                    if ( атрибуты.HasFlag( MethodAttributes.Assembly ) ) return "internal";
                    else if ( атрибуты.HasFlag( MethodAttributes.Private ) )
                        return "private";
                    else if ( атрибуты.HasFlag( MethodAttributes.Public ) )
                    {
                        return "public";
                    }
                    else if ( атрибуты.HasFlag( MethodAttributes.Family ) )
                    {
                        return "protected";
                    }

                }
                return "";
            }
        }
        public override string ToString ()
        {
            string строкаАтрибутов = АтрибутыДоступа + " " + (СобытиеИнфо.GetRemoveMethod( true ).IsStatic ? "static " : "");

            var сигнатура = строкаАтрибутов + " event "+СобытиеИнфо.EventHandlerType.Name+" " 
                + СобытиеИнфо.DeclaringType.Name+  "."+СобытиеИнфо.Name;

            return сигнатура;
        }

    }
}
